#!/bin/python

# --------------------------------------
# VIDEO BUDDHA - renamer class
# --------------------------------------
# Ranames files based on a specific regex pattern in 
#  show_list.db and based on a specific list of file 
#  extensions.
# --------------------------------------

import time
import os.path
import sys
import csv
import re

import database
import dep.thetvdbapi

class Renamer:

	def __init__ (self, console=0):
		# Variables
		# Keep in this order. 'dddd' was returing false positives when the year was in the title or
		#  under other similar circumstances (720p, 1080p, 2010, etc.)
		self.expressions = ('(\d\d\d\d)|(\d\d\d)', '(\d\d)x(\d\d)', 's(\d\d)e(\d\d)', 'S(\d\d)E(\d\d)', '(\d)x(\d\d)')
		self.dir = ''
		self.log_file = 'renamer_' + str(int(time.time())) + '.log'
		self.console = int(console)
		if self.console == 1:
			self.console = True

		# Read config shit
		self.db = database.Database()
		self.logging = int(self.db.get_option('b_log')[1])

		# Get the allowed_file_exts
		self.allowed_file_exts = self.db.get_option('video_extensions')[1]
		self.allowed_file_exts = self.allowed_file_exts.split()

		# Make the logging shit it a bool
		if self.logging == 1:
			self.logging = True
		else:
			self.logging = False

		# Create a unique log file if logging is on
		if self.logging:
			try :
				log = open(self.log_file, 'w')
				self.log("Log file created")
			except IOError as (errno, strerror):
				print "I/O error({0}): {1}".format(errno, strerror)
			except:
				print "Unexpected error while creating the log file"
				raise

	def get_path(self):
		"""CONSOLE ONLY: Get some raw_input asking for a path."""
		print("Enter a directory path (or \"exit\"):\n")
		dir = raw_input()
		if str(dir).lower() == "exit" :
			self.log("Exiting at getPath()")
			sys.exit()

		self.dir = dir
		if self.logging: self.log("Path set to: " + self.dir)

		# Path doesn't exist
		while self.path_exists(self.dir) == False:
			self.get_path()

		if self.logging: self.log_dir_list()
		return self.dir

	def path_exists(self, dir):
		"""Check if the given path exists. Return True if exists. 
		Return False if doesn't exist"""
		if os.path.exists(dir):
			if self.logging: self.log("Path exists: " + dir)
			return True

		if self.logging: self.log("Path does not exist: " + dir)
		return False

	# Only runs if logging is true
	def log_dir_list(self):		
		files = os.listdir(self.dir)

		print("Video files in: " + self.dir)
		if self.logging: self.log("List of directories and files in: " + self.dir)

		for file in files:
			if os.path.splitext(file)[1] in self.allowed_file_exts:
				if self.logging: self.log("\t" + file)
				print("\t" + file)

	def check_video_list (self):	
		files = os.listdir(self.dir)
		videoCount = 0

		for file in files :
			splitFile = os.path.splitext(file)

			if splitFile[1] in self.allowed_file_exts :
				videoCount = videoCount + 1

		if videoCount == 0 :
			print("\nNo video files were found.")
			if self.logging: self.log("No video files found in folder: " + self.dir)

			return None

		if videoCount > 0 :
			print("\nFound " + str(videoCount) + " file(s) whose file extension matched " + str(self.allowed_file_exts))
			if self.logging: self.printVideoList(self.dir)

			return videoCount

	def get_video_list(self):	
		""" Returns a list of video file in self.dir which
		are in the allowed file types"""
		videoCount = 0
		dir = self.dir
		msg = ""
		msg2 = ""
		rList = list()

		files = os.listdir(dir)

		# Run through each file in the given list
		for file in files :
			splitFile = os.path.splitext(file)

			if splitFile[1] in self.allowed_file_exts:
				rList.insert(videoCount, (dir, file))
				videoCount += 1
				if self.logging: self.log("\t" + file)

		if self.logging: self.log("Found " + str(videoCount) + " file(s) whose file extension matched " + str(self.allowed_file_exts))

		# Returns a list containing [index, (dir, file)]
		# File still has the extension
		return rList		

	def get_title(self, dir, file_old):
		""" Return the title of the given filename"""
		allowed_file_exts = self.allowed_file_exts

		data = self.db.retrieve()
		files = os.listdir(dir)

		titles = list()

		# Loop through the user's definition patterns
		for (id, oldname, newname) in data:
			splitFile = os.path.splitext(file_old)

			# Extension is allowed?
			if splitFile[1] in allowed_file_exts:			
				search = file_old.find(oldname)

				# Found a match! Wahoo
				if int(search) == 0:
					title = str(newname)

					return (dir, title, file_old)

		# No match found in the definitions
		return False

	def get_seasep_trail(self, dir_title_and_file):
		""" Get the season, episode, and trailing information of a file """
		seasep_trail = list()
		expressions = self.expressions

		print str(dir_title_and_file)

		dir = dir_title_and_file[0]
		title = dir_title_and_file[1]
		file_old = dir_title_and_file[2]

		# Run through all the expressions for Season / Episode formats
		for expression in expressions:					
			found = re.search(expression, file_old)
			if found is not None:									
				if self.logging: self.log(expression + " - Pattern match")

				seasEp = file_old[found.start():found.end()]
				split_file = os.path.splitext(file_old)
				split_file_name = split_file[0]
				split_file_ext = split_file[1]

				if self.logging: self.log("\tIn: " + str(file_old))
				if self.logging: self.log("\tMatched: " + seasEp)

				# Maku sure it doesn't include the file_old ext
				trail = split_file_name[found.end():]

				# Redo season episode format to the specified format
				# 000, or 0000
				if expression == expressions[0]:
					if len(seasEp) == 3:
						seasEp = "s0" + seasEp[0:1] + "e" + seasEp[1:3]

					if len(seasEp) == 4:
						seasEp = "s" + seasEp[0:2] + "e" + seasEp[2:4]

				# 00x00
				if expression == expressions[1]:
					seasEp = "s" + seasEp[0:2] + "e" + seasEp[3:5]

				# s00e00
				if expression == expressions[2]:
					seasEp = "s" + seasEp[1:3] + "e" + seasEp[4:6]						

				# S00E00
				if expression == expressions[3]:
					seasEp = "s" + seasEp[1:3] + "e" + seasEp[4:6]

				# 0x00
				if expression == expressions[4]:
					seasEp = "s" + seasEp[0:1] + "e" + seasEp[2:4]

				remove_chars = ['.', '_', '-', ')', ']',]
				if trail[0:1] in remove_chars:
					trail = trail[1:]

				remove_phrases = [' - ',]
				if trail[0:3] in remove_phrases:
					trail = trail[3:]

				if self.logging: self.log("\tExtracted elemenets from: " + file_old)
				if self.logging: self.log("\t\tTitle: " + title + " | SeasEp: " + seasEp + " | Trail: " + trail)

				return (dir, file_old, title, seasEp, trail)

		return False

	def get_title_matches(self):
		"""Returns a list in the form (full_filename, new_title)"""
		dir = self.dir
		allowed_file_exts = self.allowed_file_exts

		data = self.db.retrieve()
		files = os.listdir(dir)

		titles = list()

		# Loop through the user's definition patterns
		for (id, oldname, newname) in data:
			if self.logging: self.log("-------------------------")
			if self.logging: self.log("Matching: \"" + oldname + "\" to \"" + newname +"\"")
			if self.logging: self.log("-----------------------")

			# Loop through all the files in the set dir
			for file in files:
				# Make sure they match the allowed file types
				splitFile = os.path.splitext(file)

				if splitFile[1] in allowed_file_exts:			
					search = file.find(oldname)

					# Found a match! Wahoo
					if int(search) == 0:
						title = newname
						titles.append((file, title))

						if self.logging: self.log("Pattern match in: " + file)

			if self.logging: self.log("Returning (filenames, matched titles):")
			if self.logging: self.log("\t" + str(titles))

		return titles

	def get_seasep_trail_matches(self, files):
		seasep_trail = list()
		expressions = self.expressions

		# Run through all the given files, which alread have a renaming
		#  pattern match as given by get_title_matches()
		if files is not None:
			if self.logging: self.log("Looking through: ")
			if self.logging: self.log("\t" + str(files))
			for (file, title) in files:
				# Run through all the expressions for Season / Episode formats
				for expression in expressions:					
					found = re.search(expression, file)
					if found is not None:									
						if self.logging: self.log(expression + " - Pattern match")

						seasEp = file[found.start():found.end()]
						split_file = os.path.splitext(file)
						split_file_name = split_file[0]
						split_file_ext = split_file[1]

						if self.logging: self.log("\tIn: " + str(file))
						if self.logging: self.log("\tMatched: " + seasEp)

						# Maku sure it doesn't include the file ext
						trail = split_file_name[found.end():]

						# 000, or 0000
						if expression == expressions[0]:
							# Have to account for seas/ep info given in 3 digits
							if len(seasEp) == 3:
								seasEp = "s0" + seasEp[0:1] + "e" + seasEp[1:3]
							# ...or 4 digits
							if len(seasEp) == 4:
								seasEp = "s" + seasEp[0:2] + "e" + seasEp[2:4]

						# Redo season episode format to the specified format
						# 00x00
						if expression == expressions[1]:
							seasEp = "s" + seasEp[0:2] + "e" + seasEp[3:5]

						# s00e00
						if expression == expressions[2]:
							seasEp = "s" + seasEp[1:3] + "e" + seasEp[4:6]						

						# S00E00
						if expression == expressions[3]:
							seasEp = "s" + seasEp[1:3] + "e" + seasEp[4:6]

						# 0x00
						if expression == expressions[4]:
							seasEp = "s" + seasEp[0:1] + "e" + seasEp[2:4]

						# All seasep info is now in s01e01 format, can be converted from here
						seasep_format = int(self.db.get_option('seasep_format')[1])
						# 0000, 000
						if seasep_format == 4:
							seasEp = seasEp[2:3] + seasEp[4:6]
						# S01E01
						elif seasep_format == 2:
							seasEp = "S" + seasEp[1:3] + "E" + seasEp[4:6]
						# 01x01
						elif seasep_format == 3:
							seasEp = seasEp[1:3] + "x" + seasEp[4:6]
						# s01e01
						elif seasep_format == 1:
							# Already this format
							pass
						else:
							seasEp = "s" + seasEp[1:3] + "e" + seasEp[4:6]

						remove_chars = ['.', '_', '-', ')', ']']
						if trail[0:1] in remove_chars:
							trail = trail[1:]

						remove_phrases = [' - ',]
						if trail[0:3] in remove_phrases:
							trail = trail[3:]

						if self.logging: self.log("\tExtracted elemenets from: " + file)
						if self.logging: self.log("\t\tTitle: " + title + " | SeasEp: " + seasEp + " | Trail: " + trail)

						seasep_trail.append((file, (title, seasEp, trail)))

			return seasep_trail	

	def get_rename_list(self):
		titles = self.get_title_matches()
		file_info = self.get_seasep_trail_matches(titles)

		renamable_files = list()

		renaming_scheme = self.db.get_option('renaming_scheme')[1]

		if self.logging: self.log("Looking for renamable files in:")
		if self.logging: self.log("\t" + str(file_info))

		# Run through them all to build the list
		if file_info is not None:
			for (file, (title, seasep, trail)) in file_info:
				new_name = renaming_scheme.replace('%t', title)
				new_name = new_name.replace('%se', seasep)
				new_name = new_name.replace('%i', trail)
				new_name = new_name + os.path.splitext(file)[1]

				if self.logging: self.log("Able to rename: " + file)
				if self.logging: self.log("\tTo: " + new_name)

				renamable_files.append((file, new_name))

		return renamable_files

	def rename_file(self, dir, file, renameFile):
		# Attempt to rename files
		if self.logging: self.log("Trying to rename files.")

		try:
			src = os.path.join(dir, file)
			dest = os.path.join(dir, renameFile)

			if self.logging: self.log("Rename src: " + src)
			if self.logging: self.log("Rename dst: " + dest)

			if os.rename(src, dest):
				if self.logging: self.log("Renamed: " + file)
				if self.logging: self.log("\tTo: " + renameFile)

				if self.console: print("Renamed: " + file)
				if self.console: print("\tTo: " + renameFile)			

				return True

		except OSError:
			if self.logging: self.log("The file " + file +  "could not be renamed to " + renameFile)

			return False

	def set_dir(self, dir):
		self.dir = dir

		if self.logging: self.log("Dir set to: " + dir)

	def set_show_list(self, file):
		self.show_list = file

		if self.logging: self.log("Show list set to: " + file)

	# Not used anymore, keep it for legacy I spose
	def check_show_list(self):
		file = self.show_list

		try:
			file = open(file, 'r')

			try:
				showListFileData = file.read()			
				showListFileData = showListFileData.replace('\n', '').rstrip('\n\r;')

				showListFileData = dict(csv.reader([item], delimiter = '=', quotechar = "'").next() 
					                  for item in csv.reader([showListFileData], delimiter = ';', quotechar = "'").next())

			finally:
				file.close()

				return True

		except IOError:
			self.log("Could not open show list for reading.")

			return False

	def output_show_list(self):
		file = self.show_list

		try:
			file = open(file, 'r')

			try:
				showListFileData = file.read()			
				showListFileData = showListFileData.replace('\n', '').rstrip('\n\r;')

				showListFileData = dict(csv.reader([item], delimiter = '=', quotechar = "'").next() 
					                  for item in csv.reader([showListFileData], delimiter = ';', quotechar = "'").next())

			finally:
				file.close()

				return showListFileData

		except IOError:
			self.log("Could not open show list for reading.")

			return False

	def log(self, msg):
		# Open and append the log file
		try :
			log = open(self.log_file, 'a')
			log.write(time.strftime("%Y-%m-%d %H:%M:%S") + ": " + msg + "\n")
		except IOError as (errno, strerror):
			print "I/O error({0}): {1}".format(errno, strerror)
		except:
			print "Unexpected error:", sys.exc_info()[0]
			raise